﻿#region Disclaimer / License
// Copyright (C) 2011, Kenneth Bergeron, IAP Worldwide Services, Inc
// NOAA :: National Marine Fisheries Service
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
// 
#endregion
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Utility
{
    /// <summary>
    /// Control to combine the worst traits of a treeview and a datagridview
    /// </summary>
    public partial class TreeColumnsView : UserControl
    {
        // Backing
        private DataGridView itsDataGridView = null;
        /// <summary>
        /// DataGridView to glam onto
        /// </summary>
        public DataGridView DataGridView 
        {
            get { return itsDataGridView; }
            set
            {
                itsDataGridView = value;
                itsDataGridView.Parent = this.DGVPanel;
                itsDataGridView.Dock = DockStyle.Fill;
                itsDataGridView.Margin = new Padding(0);
                this.TreeTableLayoutPanel.RowStyles[0].Height = this.DataGridView.ColumnHeadersHeight;
                this.TreeTableLayoutPanel.PerformLayout();
                this.treeView1.ItemHeight = this.DataGridView.RowTemplate.Height;
                this.DummyDataGridView.ColumnHeadersHeight = this.DataGridView.ColumnHeadersHeight;
                this.DummyDataGridView.Font = this.DataGridView.Font;
                this.treeView1.BackColor = this.DataGridView.BackgroundColor;
            }
        }
        /// <summary>
        /// Dont update
        /// </summary>
        private volatile bool Suspended = false;
        /// <summary>
        /// Suspend updates
        /// </summary>
        public void Suspend() 
        { 
            this.Suspended = true;
            this.treeView1.Scrollable = false;
        }
        /// <summary>
        /// Resume updates
        /// </summary>
        public void Resume() 
        { 
            Suspended = false;
            this.treeView1.Scrollable = true;
            this.Invalidate();
        }
        /// <summary>
        /// A treeview with a OnScroll event
        /// </summary>
        private class TreeViewBastard : TreeView
        {
            /// <summary>
            /// When TreeViews Scroll...
            /// </summary>
            public event EventHandler Scroll;
            /// <summary>
            /// A treeview with a OnScroll event
            /// </summary>
            public TreeViewBastard()
                : base()
            {
            }
            /// <summary>
            /// Does something to a node
            /// </summary>
            /// <param name="aNode">Node to do it to</param>
            public delegate void DoToDelegate(TreeNode aNode);
            /// <summary>
            /// Does somthing to every visible node
            /// </summary>
            /// <param name="aAction">What to do to it</param>
            public void DoToVisibleNodes(DoToDelegate aAction)
            {
                DoToVisibleNodes(aAction, this.Nodes);
            }
            /// <summary>
            /// Does something to every visible node
            /// </summary>
            /// <param name="aAction">What to do</param>
            /// <param name="aNodes">Nodes to do it to (recursive)</param>
            public void DoToVisibleNodes(DoToDelegate aAction, TreeNodeCollection aNodes)
            {
                foreach (TreeNode Node in aNodes)
                {
                    if (Node.IsVisible) aAction(Node);
                    DoToVisibleNodes(aAction, Node.Nodes);
                }
            }
            /// <summary>
            /// Catch the scroll message and issue event
            /// </summary>
            protected override void WndProc(ref Message m)
            {
                if ((m.Msg == 0x0115 || m.Msg == 0x0114 || m.Msg == 0x020A) && Scroll != null) OnScroll(); 
                base.WndProc(ref m);
            }
            /// <summary>
            /// The event thingy
            /// </summary>
            protected void OnScroll()
            {
                this.Invalidate();
                if (Scroll != null) Scroll(this, new EventArgs());
            }
        }
        /// <summary>
        /// Control to combine the worst traits of a treeview and a datagridview
        /// </summary>
        public TreeColumnsView()
        {
            InitializeComponent();
            this.treeView1.DrawMode = TreeViewDrawMode.OwnerDrawText;
            this.treeView1.DrawNode += new DrawTreeNodeEventHandler(treeView1_DrawNode);
            this.treeView1.Scroll += new EventHandler(delegate(object sender, EventArgs e) { this.Invalidate(); });
            this.treeView1.AfterCollapse += new TreeViewEventHandler(delegate(object sender, TreeViewEventArgs e) { this.Invalidate(); });
            this.treeView1.AfterExpand += new TreeViewEventHandler(delegate(object sender, TreeViewEventArgs e) { this.Invalidate(); });
        }
        /// <summary>
        /// Expose the double click
        /// </summary>
        public event TreeNodeMouseClickEventHandler NodeDoubleClicked;
        /// <summary>
        /// A list of all the nodes in the treeview
        /// </summary>
        public TreeNodeCollection Nodes { get { return this.treeView1.Nodes; } }
        /// <summary>
        /// text to put in the tree part of the control
        /// </summary>
        public string TreeHeaderText
        {
            get { return this.DummyDataGridView.Columns[0].HeaderText; }
            set { this.DummyDataGridView.Columns[0].HeaderText = value; }
        }
        /// <summary>
        /// How wide's the tree
        /// </summary>
        public float TreeWidth
        {
            get { return this.TreeTableLayoutPanel.ColumnStyles[0].Width; }
            set { this.TreeTableLayoutPanel.ColumnStyles[0].Width = value; }
        }
        /// <summary>
        /// Draw a node
        /// </summary>
        void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            // Dont know why, but sometimes get these weird zero-height events
            if (e.Bounds.Height <= 0) return;
            // Extend the bounds all the way across the control
            Rectangle Grid = Rectangle.FromLTRB(e.Bounds.Left, e.Bounds.Top, this.treeView1.Right, e.Bounds.Bottom);
            // Erase what's there
            using (Brush Brush = new SolidBrush(this.DataGridView.DefaultCellStyle.BackColor))
                e.Graphics.FillRectangle(Brush, Grid);
            // Draw the string
            using (Brush Brush = new SolidBrush(this.DataGridView.DefaultCellStyle.ForeColor))
                e.Graphics.DrawString(e.Node.Text, this.DataGridView.Font, Brush, 
                    new Point(Grid.Location.X+2, Grid.Location.Y+2));
            // Draw the attractive border
            using (Pen Pen = new Pen(this.DataGridView.GridColor))
                e.Graphics.DrawRectangle(Pen, Grid);
        }
        /// <summary>
        /// Make a new node, the Values are placed in the DataGridView
        /// </summary>
        /// <param name="aNodeName">Name of new node</param>
        /// <param name="Values">Values to put into the DataGridView</param>
        /// <returns></returns>
        public TreeNode NewNode(string aNodeName, params object[] Values)
        {
            return new TreeNode(aNodeName) { Tag = Values };
        }
        /// <summary>
        /// Refresh the table on paint
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (!Suspended) RefreshTable();
            base.OnPaint(e);
        }
        /// <summary>
        /// Make sure the DataGridView matches the tree
        /// </summary>
        public void RefreshTable()
        {
            if (this.DataGridView == null) return;
            this.DataGridView.Rows.Clear();
            this.treeView1.DoToVisibleNodes(delegate(TreeNode aNode) { this.DataGridView.Rows.Add((object[])aNode.Tag); });
        }
        /// <summary>
        /// Tell that User double clicked
        /// </summary>
        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (this.NodeDoubleClicked != null) this.NodeDoubleClicked(this, e);
        }

    }
}
